/*****************************************************************************
 *                                                                           *
 * Module:       Altera_UP_Character_Buffer                                  *
 * Description:                                                              *
 *     This module holds the information to be display on the screen for the *
 *   character mode.                                                         *
 *                                                                           *
 *****************************************************************************/

module Altera_UP_Character_Buffer (
	// Inputs
	clk,
	reset,

	address,
	chipselect,
	read,
	write,
	writedata,

//	character,
	x_coordinate,
	y_coordinate,
	
	// Bidirectionals

	// Outputs
	readdata,
//	readdatavalid,
//	waitrequest,

	character_data
);

/*****************************************************************************
 *                           Parameter Declarations                          *
 *****************************************************************************/

parameter CHAR_ADDR_WIDTH				= 13;
parameter CHAR_DATA_WIDTH				= 16;
parameter CHAR_BUFFER_SIZE				= 4800;
parameter CHAR_ADDR_INCREMENT			= 13'h0001;

parameter Y_COORD_SMALL_OFFSET			= 4'h0;
parameter Y_COORD_LARGE_OFFSET			= 6'h00;

parameter CHAR_BITS						= 4;
parameter ENLARGE_CHAR					= 0;

// The Character Slave Bit Allocation
parameter X_BITS						= 7;
parameter Y_BITS						= 6;

// The Blank Char is space
parameter BLANK_CHAR					= 16'h0020;

/*****************************************************************************
 *                             Port Declarations                             *
 *****************************************************************************/
// Inputs
input				clk;
input				reset;

input		[(CHAR_ADDR_WIDTH-1):0]			address;
input				chipselect;
input				read;
input				write;
input		[31:0]	writedata;

input		[NUMBER_OF_BITS_FOR_X_COORD:1]	x_coordinate;
input		[NUMBER_OF_BITS_FOR_Y_COORD:1]	y_coordinate;

// Bidirectionals

// Outputs
output	reg	[31:0]	readdata;
//output	reg			readdatavalid;
//output				waitrequest;

output				character_data;

/*****************************************************************************
 *                           Constant Declarations                           *
 *****************************************************************************/

localparam NUMBER_OF_BITS_FOR_X_COORD	= 10;
localparam NUMBER_OF_BITS_FOR_Y_COORD	= 9;

/*****************************************************************************
 *                 Internal wires and registers Declarations                 *
 *****************************************************************************/
// Internal Wires
wire		[15:0]	current_character;

wire		[15:0]	data_from_char_buffer;

// Internal Registers
reg			[CHAR_ADDR_WIDTH:1]				vga_screen_location;
reg			[CHAR_ADDR_WIDTH:1]				slave_screen_location;

reg			[15:0]	new_character;
reg 				write_to_screen;
reg			[8:0]	delayed_x_position;
reg			[8:0]	delayed_y_position;

reg					clear_display;

// State Machine Registers

/*****************************************************************************
 *                         Finite State Machine(s)                           *
 *****************************************************************************/


/*****************************************************************************
 *                             Sequential logic                              *
 *****************************************************************************/

always @(posedge clk)
begin
	if (reset == 1'b1)
	begin
		delayed_x_position <= 9'h000;
		delayed_y_position <= 9'h000;
	end
	else
	begin
		delayed_x_position <= {delayed_x_position[5:0], 
			x_coordinate[(ENLARGE_CHAR+3):(ENLARGE_CHAR+1)]};
		delayed_y_position <= {delayed_y_position[5:0], 
			y_coordinate[(ENLARGE_CHAR+3):(ENLARGE_CHAR+1)]};
	end
end

always @(posedge clk)
begin
	if (reset == 1'b1)
		vga_screen_location <= {CHAR_ADDR_WIDTH{1'b0}};
	else
		vga_screen_location <= x_coordinate[NUMBER_OF_BITS_FOR_X_COORD:CHAR_BITS] +
			{y_coordinate[NUMBER_OF_BITS_FOR_Y_COORD:CHAR_BITS], Y_COORD_SMALL_OFFSET} +
			{y_coordinate[NUMBER_OF_BITS_FOR_Y_COORD:CHAR_BITS], Y_COORD_LARGE_OFFSET};
end

always @(posedge clk)
begin
	if (reset == 1'b1)
		slave_screen_location <= {CHAR_ADDR_WIDTH{1'b0}};
	else if (clear_display == 1'b1)
		slave_screen_location <= slave_screen_location + CHAR_ADDR_INCREMENT;
	else if (chipselect == 1'b1)
		slave_screen_location <= address[(X_BITS-1):0] +
			{address[(Y_BITS + X_BITS - 1):X_BITS], Y_COORD_SMALL_OFFSET} + 
			{address[(Y_BITS + X_BITS - 1):X_BITS], Y_COORD_LARGE_OFFSET};
	else
		slave_screen_location <= {CHAR_ADDR_WIDTH{1'b0}};
end

always @(posedge clk)
begin
	if (reset == 1'b1)
	begin
		new_character <= 16'h0000;
		write_to_screen <= 1'b0;
	end
	else if ((chipselect == 1'b1) && (write == 1'b1)) // && (address == 1'b1))
	begin
		new_character <= writedata[15:0];
		write_to_screen <= 1'b1;
	end
	else
	begin
		write_to_screen <= 1'b0;
	end
end

always @(posedge clk)
begin
	if (reset == 1'b1)
		clear_display <= 1'b1;
	else if ((chipselect == 1'b1) && (write == 1'b1) && (&(address) == 1'b1))
		clear_display <= 1'b1;
	else if (slave_screen_location == (CHAR_BUFFER_SIZE - 1))
		clear_display <= 1'b0;
end

always @(posedge clk)
begin
	if (reset == 1'b1)
		readdata <= 32'h00000000;
	else if ((clear_display == 1) || (&(address) == 1'b1))
		readdata <= {31'h00000000, clear_display};
	else
		readdata <= {16'h0000, data_from_char_buffer};
end

/*****************************************************************************
 *                            Combinational logic                            *
 *****************************************************************************/


/*****************************************************************************
 *                              Internal Modules                             *
 *****************************************************************************/

altsyncram	Char_Buffer_Memory (
	// Inputs
	.clock0			(clk),
	.address_a		(slave_screen_location),
	.wren_a			(write_to_screen | clear_display),
	.data_a			((clear_display) ? BLANK_CHAR : new_character),

	.clock1			(clk),
	.address_b		(vga_screen_location),

	// Bidirectionals

	// Outputs
	.q_a			(data_from_char_buffer),
	.q_b			(current_character),
	
	// Unused 
	.rden_b			(1'b1),
	.wren_b			(1'b0),
	.data_b			({16{1'b1}}),

	.aclr0			(1'b0),
	.aclr1			(1'b0),
	.clocken1		(1'b1),
	.clocken0		(1'b1),
	.byteena_a		(1'b1),
	.byteena_b		(1'b1),
	.addressstall_a	(1'b0),
	.addressstall_b	(1'b0)
);
defparam
	Char_Buffer_Memory.init_file							= "UNUSED",
	Char_Buffer_Memory.intended_device_family				= "Cyclone II",
	Char_Buffer_Memory.lpm_type								= "altsyncram",
	Char_Buffer_Memory.operation_mode						= "BIDIR_DUAL_PORT",
	Char_Buffer_Memory.read_during_write_mode_mixed_ports	= "DONT_CARE",
	Char_Buffer_Memory.power_up_uninitialized				= "FALSE",
	Char_Buffer_Memory.address_reg_b						= "CLOCK1",
	Char_Buffer_Memory.wrcontrol_wraddress_reg_b			= "CLOCK1",
	Char_Buffer_Memory.clock_enable_input_a					= "BYPASS",
	Char_Buffer_Memory.clock_enable_input_b					= "BYPASS",
	Char_Buffer_Memory.clock_enable_output_a				= "BYPASS",
	Char_Buffer_Memory.clock_enable_output_b				= "BYPASS",
	Char_Buffer_Memory.numwords_a							= CHAR_BUFFER_SIZE,
	Char_Buffer_Memory.numwords_b							= CHAR_BUFFER_SIZE,
	Char_Buffer_Memory.outdata_aclr_a						= "NONE",
	Char_Buffer_Memory.outdata_aclr_b						= "NONE",
	Char_Buffer_Memory.outdata_reg_a						= "CLOCK0",
	Char_Buffer_Memory.outdata_reg_b						= "CLOCK1",
	Char_Buffer_Memory.widthad_a							= CHAR_ADDR_WIDTH,
	Char_Buffer_Memory.widthad_b							= CHAR_ADDR_WIDTH,
	Char_Buffer_Memory.width_a								= CHAR_DATA_WIDTH,
	Char_Buffer_Memory.width_b								= CHAR_DATA_WIDTH,
	Char_Buffer_Memory.width_byteena_a						= 1,
	Char_Buffer_Memory.width_byteena_b						= 1;


Altera_UP_128_Character_Rom Character_Rom (
	// Inputs
	.clk			(clk),

	.character		(current_character[6:0]),
	.x_coordinate	(delayed_x_position[8:6]),
	.y_coordinate	(delayed_y_position[8:6]),
	
	// Bidirectionals

	// Outputs
	.character_data	(character_data)
);
	
endmodule
